package com.netty.example.discard;

import com.netty.example.echo.EchoServerHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * Discards any incoming data
 */
public class DiscardServer {
    private int port;

    public DiscardServer(int port) {
        this.port = port;
    }

    public void run() throws InterruptedException {
        // 1 是处理 I/O 操作的多线程时间循环
        /*
         Netty EventLoopGroup为不同类型的传输提供了各种实现。在此示例中，我们正在实现服务器端应用程序，
         因此NioEventLoopGroup将使用两个应用程序。第一个通常称为“老板”，接受传入的连接。第二个通常称为“工人”，
         一旦老板接受连接并将注册的连接注册给工人，便处理已接受连接的流量。使用多少线程以及如何将它们映射到创建的
         Channels取决于EventLoopGroup实现，甚至可以通过构造函数进行配置。
         */
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            /*
            2.ServerBootstrap是设置服务器的帮助程序类
            您可以使用Channel直接设置服务器。但是，请注意，这是一个繁琐的过程，在大多数情况下您无需这样做。
             */
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    /*
                    3. 在这里，我们指定使用NioServerSocketChannel用于实例化新类Channel以接受传入连接的类。
                     */
                    .channel(NioServerSocketChannel.class)
                    /*
                    4. 在此指定的处理程序将始终调用该Channel。该ChannelInitializer是意要帮助用户配置
                    一个新的特殊处理程序Channel。您很可能希望通过添加一些处理程序（例如实现您的网络应用程序）
                    来配置ChannelPipeline新程序的。随着应用程序变得复杂，您可能会向管道中添加更多处理程序，
                    并最终将此匿名类提取到顶级类中。ChannelDiscardServerHandler
                     */
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new EchoServerHandler(), new DiscardServerHandler());
                        }
                        /*
                        5. 您还可以设置特定于Channel实现的参数。我们正在编写一个TCP / IP服务器，
                        因此我们可以设置套接字选项，例如tcpNoDelay和keepAlive。请参考的apidocsChannelOption
                        和特定的ChannelConfig实现以获取有关support的概述ChannelOption。
                         */
                    }).option(ChannelOption.SO_BACKLOG, 128)
                    /*
                    6. 你有没有注意到option()和childOption()？option()用于NioServerSocketChannel
                    接受传入的连接。childOption()是针对Channel父级接受的ServerChannel，NioSocketChannel
                    在这种情况下。
                     */
                      .childOption(ChannelOption.SO_KEEPALIVE,true); // 6
            // Bind and start to accept incoming connections
            /*
            7 我们现在准备出发。剩下的就是绑定到端口并启动服务器。在这里，我们绑定到计算机8080中所有NIC（网络接口卡）的端口。
            现在，您可以bind()根据需要多次调用该方法（使用不同的绑定地址。）
             */
            ChannelFuture f = b.bind(port).sync(); // 7

            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        int port = 8080;
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        }

        new DiscardServer(port).run();
    }
}
